import { isObject, uniqueId } from 'lodash';

export class Utils {
  // 监听事件
  public static _listenEvents: object = {};

  public static listenBody(eventName: string, callback: (...args: any[]) => any): (...args: any[]) => any {
    return Utils.listen('body', eventName, callback);
  }

  public static listenDocument(eventName: string, callback: (...args: any[]) => any): (...args: any[]) => any {
    return Utils.listen('document', eventName, callback);
  }

  /**
   * 添加监听事件, 相同元素的相同事件只会注册一次, 避免触发多次脏值检测
   * 使用 element + '_' + eventName 作为唯一标识, 如document, body, #domid, .domclass 请确保唯一性
   * @element any, document 或者 dom选择器, document.querySelector(@element)
   * @eventName string, 事件名称, addEventListener(@eventName)
   * @callback (...args: any[]) => any, 回调, 回调时会传入event
   * @return (...args: any[]) => any, 取消监听函数
   */
  public static listen(element: any, eventName: string, callback: (...args: any[]) => any): (...args: any[]) => any {
    const dom =
      element === 'document' || (isObject(element) && element.nodeName === '#document')
        ? document
        : document.querySelector(element);

    const key = `${element}_${eventName}`;

    let registerdEvent = Utils._listenEvents[key];

    if (!registerdEvent) {
      // 首次监听, 调用addEventListener注册
      Utils._listenEvents[key] = {
        listeners: [],
      };
      registerdEvent = Utils._listenEvents[key];

      // 调用已注册的回调
      registerdEvent.handler = (event): void => {
        registerdEvent.listeners.forEach((listener): void => {
          if (listener.callback) {
            listener.callback(event);
          }
        });
      };

      dom.addEventListener(eventName, registerdEvent.handler, false);
    }

    // 注册回调
    const newListener = {
      key: uniqueId(key),
      callback,
    };

    registerdEvent.listeners.push(newListener);

    return (): void => {
      // 清除当前事件回调
      registerdEvent.listeners = registerdEvent.listeners.filter((e): boolean => e.key !== newListener.key);

      // 当前事件回调为空时, 解除事件绑定
      if (registerdEvent.listeners.length === 0) {
        dom.removeEventListener(eventName, registerdEvent.handler, false);
        Utils._listenEvents[key] = null;
      }
    };
  }
}
